#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>

int pcm16le_split(const char *url);
int pcm16e_halfVolumeLeft(const char *url);
int pcm16_doubleSpeed(const char *url);
int pcm16le_to_pcm8(const char *url);
int pcm16le_cur_singaleChannel(const char *url, int start_num, int dur_num);
int pcm16le_to_wave(const char *pcmPath, int channels, int sample_rate, const char *wavepath);

int simplest_pcm16le_to_wave(const char *pcmpath, int channels, int sample_rate, const char *wavepath)
{
    /**
     * @brief wave 文件一般由 3 个区块组成：RIFF chunk、Format chunk 和 Data chunk。其中chunk(块)是最小单位进行存储
     * 
     */

    typedef struct WAVE_HEADER
    {
        char fccID[4];        //‘RIFF’
        unsigned long dwSize; //‘fileSize - ID - Type’
        char fccType[4];      //‘WAVE’
    } WAVE_HEADER;

    typedef struct WAVE_FMT
    {
        char ID[4];                  //‘fmt’
        unsigned long dwSize;        //该区块数据长度「不包括ID 和 Size」
        unsigned short wAudioFormat; //‘Data 区块存储的音频数据的格式，PCM 音频数据的值为1’
        unsigned short wChannels;    //‘音频数据的声道数， 1:单声道 2:双声道’

        unsigned long dwSampleRate;     //‘音频数据的采样率’ //是指录音设备在一秒钟内对声音信号的采样次数。
        unsigned long dwByteRate;       //‘每秒数据的字节数= 音频数据采样率 * 音频数据声道数 * 每个采样存储的bit 数 / 8 ’
        unsigned short wBlockAlign;     //每个采样所需要的字节数 = 通道数 * 每个采样存储的bit 数 / 8
        unsigned short uiBitsPerSample; //每个采样存储的bit 数
    } WAVE_FMT;

    typedef struct WAVE_DATA
    {
        char fccID[4];        //'data'
        unsigned long dwSize; //音频数据长度
    } WAVE_DATA;

    if (channels == 0 || sample_rate == 0)
    {
        channels = 2;
        sample_rate = 44100;
    }
    int bits = 16;

    WAVE_HEADER pcmHEADER;
    WAVE_FMT pcmFMT;
    WAVE_DATA pcmDATA;

    unsigned short m_pcmData;
    FILE *fp, *fpout;

    fp = fopen(pcmpath, "rb");
    if (fp == NULL)
    {
        printf("open pcm file error\n");
        return -1;
    }
    fpout = fopen(wavepath, "wb+");
    if (fpout == NULL)
    {
        printf("create wav file error\n");
        return -1;
    }
    //WAVE_HEADER
    memcpy(pcmHEADER.fccID, "RIFF", strlen("RIFF"));
    memcpy(pcmHEADER.fccType, "WAVE", strlen("WAVE"));
    fseek(fpout, sizeof(WAVE_HEADER), 1);
    //WAVE_FMT
    pcmFMT.dwSampleRate = sample_rate;
    pcmFMT.dwByteRate = pcmFMT.dwSampleRate * channels * bits / 8;
    pcmFMT.uiBitsPerSample = bits;
    memcpy(pcmFMT.ID, "fmt ", strlen("fmt "));
    pcmFMT.dwSize = 16;
    pcmFMT.wBlockAlign = channels * bits / 8;
    pcmFMT.wChannels = channels;
    pcmFMT.wAudioFormat = 1;

    fwrite(&pcmFMT, sizeof(WAVE_FMT), 1, fpout);
    fpos_t pos;
    fgetpos(fpout, &pos);

    //WAVE_DATA;
    memcpy(pcmDATA.fccID, "data", strlen("data"));
    pcmDATA.dwSize = 0;
    fseek(fpout, sizeof(WAVE_DATA), SEEK_CUR);
    fgetpos(fpout, &pos);

    fread(&m_pcmData, sizeof(unsigned short), 1, fp);
    while (!feof(fp))
    {
        pcmDATA.dwSize += 2;
        fwrite(&m_pcmData, sizeof(unsigned short), 1, fpout);
        fread(&m_pcmData, sizeof(unsigned short), 1, fp);
    }

    pcmHEADER.dwSize = 44 + pcmDATA.dwSize;

    rewind(fpout);
    fgetpos(fpout, &pos);
    fwrite(&pcmHEADER, sizeof(WAVE_HEADER), 1, fpout);
    fseek(fpout, sizeof(WAVE_FMT), SEEK_CUR);
    fgetpos(fpout, &pos);
    fwrite(&pcmDATA, sizeof(WAVE_DATA), 1, fpout);

    fclose(fp);
    fclose(fpout);

    return 0;
}

int main(void)
{

    // pcm16le_split("../pcm/resource/NocturneNo2inEflat_44.1k_s16le.pcm");
    // pcm16e_halfVolumeLeft("../pcm/resource/NocturneNo2inEflat_44.1k_s16le.pcm");
    // pcm16_doubleSpeed("../pcm/resource/NocturneNo2inEflat_44.1k_s16le.pcm");
    // pcm16le_to_pcm8("../pcm/resource/NocturneNo2inEflat_44.1k_s16le.pcm");
    // pcm16le_cur_singaleChannel("../pcm/resource/drum.pcm", 2360, 1200);
    pcm16le_to_wave("../pcm/resource/NocturneNo2inEflat_44.1k_s16le.pcm", 2, 44100, "output_nocturne.wav");
    // simplest_pcm16le_to_wave("../pcm/resource/NocturneNo2inEflat_44.1k_s16le.pcm", 2, 44100, "output_nocturne.wav");
    printf("end");
    return 0;
}

int pcm16le_split(const char *url)
{
    FILE *fp = fopen(url, "rb+");
    if (!fp)
    {
        printf("fopen:[%s]\n", strerror(errno));
        return -1;
    }
    FILE *fp1 = fopen("output_l.pcm", "wb+");
    FILE *fp2 = fopen("output_r.pcm", "wb+");

    uint8_t *sample = (uint8_t *)malloc(4);

    while (!feof(fp))
    {
        fread(sample, 1, 4, fp);

        fwrite(sample, 1, 2, fp1);

        fwrite(sample + 2, 1, 2, fp2);
    }

    free(sample);
    fclose(fp);
    fclose(fp1);
    fclose(fp2);

    return 0;
}

int pcm16e_halfVolumeLeft(const char *url)
{
    FILE *fp = fopen(url, "rb+");
    if (!fp)
    {
        printf("fopen:[%s]\n", strerror(errno));
        return -1;
    }
    FILE *fp1 = fopen("output_halfleft.pcm", "wb+");

    int cnt = 0;

    uint8_t *sample = (uint8_t *)malloc(4);

    while (!feof(fp))
    {
        short *sampleNum = NULL;
        fread(sample, 1, 4, fp);

        sampleNum = (short *)sample;
        *sampleNum = *sampleNum / 2;

        //L
        fwrite(sample, 1, 2, fp1);
        //R
        fwrite(sample + 2, 1, 2, fp1);

        cnt++;
    }

    printf("Sample Cnt:%d\n", cnt);

    free(sample);
    fclose(fp);
    fclose(fp1);

    return 0;
}

int pcm16_doubleSpeed(const char *url)
{
    FILE *fp = fopen(url, "rb+");
    if (!fp)
    {
        printf("fopen:[%s]\n", strerror(errno));
        return -1;
    }
    FILE *fp1 = fopen("output_doubleSpeed.pcm", "wb+");
    int cnt = 0;

    unsigned char *sample = (unsigned char *)malloc(4);

    while (!feof(fp))
    {

        fread(sample, 1, 4, fp);

        if (cnt % 2)
        {
            //L
            fwrite(sample, 1, 2, fp1);
            //R
            fwrite(sample + 2, 1, 2, fp1);
        }
        cnt++;
    }
    printf("Sample Cnt:%d\n", cnt);

    free(sample);
    fclose(fp);
    fclose(fp1);
    return 0;
}

int pcm16le_to_pcm8(const char *url)
{
    FILE *fp = fopen(url, "rb+");
    if (!fp)
    {
        printf("fopen:[%s]\n", strerror(errno));
        return -1;
    }
    FILE *fp1 = fopen("output_8.pcm", "wb+");
    int cnt = 0;

    unsigned char *sample = (unsigned char *)malloc(4);

    while (!feof(fp))
    {
        short *sampleNum16 = NULL;
        char sampleNum8 = 0;
        uint8_t sampleNum8_u = 0;

        fread(sample, 1, 4, fp);

        sampleNum16 = (short *)sample;    //左声道
        sampleNum8 = (*sampleNum16) >> 8; //将有符号的 short（16bit） 转换为 有符号的 char（8bit）
        sampleNum8_u = sampleNum8 + 128;  //将 有符号的 char(int8_t) 转换为 无符号的 char(uint8_t)
        // printf("[%d : %u] \n", sampleNum8, sampleNum8_u);
        fwrite(&sampleNum8_u, 1, 1, fp1);

        sampleNum16 = (short *)(sample + 2); //右声道
        sampleNum8 = (*sampleNum16) >> 8;
        sampleNum8_u = sampleNum8 + 128;
        fwrite(&sampleNum8_u, 1, 1, fp1);

        cnt++;
    }
    printf("Sample Cnt:%d\n", cnt);

    free(sample);
    fclose(fp);
    fclose(fp1);
}

int pcm16le_cur_singaleChannel(const char *url, int start_num, int dur_num)
{
    FILE *fp = fopen(url, "rb+");
    if (!fp)
    {
        printf("fopen:[%s]\n", strerror(errno));
        return -1;
    }
    FILE *fp1 = fopen("output_cut.pcm", "wb+");
    FILE *fp_stat = fopen("output_cut.txt", "wb+");

    uint8_t *sample = (uint8_t *)malloc(2);
    int cnt = 0;
    while (!feof(fp))
    {
        fread(sample, 1, 2, fp);
        if (cnt > start_num && cnt <= (start_num + dur_num))
        {
            fwrite(sample, 1, 2, fp1);

            short sampleNum = sample[1];
            sampleNum = sampleNum * 256;
            sampleNum = sampleNum + sample[0];

            fprintf(fp_stat, "%6d,", sampleNum);
            if (cnt % 10 == 0)
            {
                fprintf(fp_stat, "\n", sampleNum);
            }
        }

        cnt++;
    }

    free(sample);
    fclose(fp);
    fclose(fp1);
    fclose(fp_stat);
    return 0;
}
/**
 * RIFF（资源交换档案标准（Resource Interchange File Format））
 * RIFF 文件结构是由一块一块（Chunk）组成的。每个块都有标志符，数据大小以及数据组成。
 * 
 * WAV 是 Microsoft 开发的一种音频格式，它符合上面提到的RIFF 文件格式标准，可以看作是一个RIFF 文件的具体实例。
 * 既然WAV 符合RIFF 规范，其基本的组成单元也是chunk。一个WAV 文件通常由三个 chunk 以及一个可选的chunk，
 * 其在文件中的排列方式依次是：RIFF Chunk， Format Chunk， Fact Chunk（可选），Data Chunk
*/

///wav 是一个音频格式，该格式的实质就是在PCM文件前加了一个描述视频参数的头部信息
int pcm16le_to_wave(const char *pcmPath, int channels, int sample_rate, const char *wavepath)
{
    typedef struct RIFF_Chunk
    {
        char fccID[4];        //大端 标识符 内容为 'R''I''F''F'
        unsigned long dwSize; // 整个文件的长度减去`ID`和`SIZE`的长度
        char fccType[4];      //大端  'W''A''V''E'
    } RIFF_Chunk;

    typedef struct WAVE_FMT
    {
        char fccID[4];                 //大端 表示符 内容：'F' 'M' 'T' ' '
        unsigned long dwSize;          // 16 表示该区块数据的长度（不包含ID和Size的长度）
        uint16_t u16FormatTag;         //音频格式 表示Data区块存储的音频数据的格式，PCM音频数据的值为1
        uint16_t u16Channels;          //声道数 表示音频数据的声道数，1：单声道，2：双声道
        unsigned long dwSamplePerSec;  //‘音频数据的采样率’ //是指录音设备在一秒钟内对声音信号的采样次数。
        unsigned long dwAVgBytePerSec; //‘每秒数据的字节数= 音频数据采样率 * 音频数据声道数 * 每个采样存储的bit 数 / 8 ’
        uint16_t u16BlockAlign;        //每个采样所需要的字节数 = 通道数 * 每个采样存储的bit 数 / 8
        uint16_t u16BitsPerSample;     //每个采样存储的bit 数
    } WAVE_FMT;

    typedef struct WAVE_DATA
    {
        char fccID[4];        //以'data'为标识
        unsigned long dwSize; //音频数据长度
    } WAVE_DATA;

    if (channels == 0 || sample_rate == 0)
    {
        channels = 2;
        sample_rate = 44100;
    }

    int bits = 16;

    RIFF_Chunk pcmHeader;
    WAVE_FMT pcmFMT;
    WAVE_DATA pcmData;

    uint16_t m_pcmData;
    FILE *fp, *fpOut;

    fp = fopen(pcmPath, "rb");
    if (!fp)
    {
        printf("fopen:[%s]\n", strerror(errno));
        return -1;
    }

    fpOut = fopen(wavepath, "wb+");
    if (!fpOut)
    {
        printf("fopen:[%s]\n", strerror(errno));
        return -1;
    }

    ///RIFF_Chunk
    memcpy(pcmHeader.fccID, "RIFF", strlen("RIFF"));
    memcpy(pcmHeader.fccType, "WAVE", strlen("WAVE"));
    fpos_t pos;
    fseek(fpOut, sizeof(RIFF_Chunk), 1); //为什么要修改 输出文件的文件指针，而移动1 + RIFF 块，接下来的指针在格式块的开头
    fgetpos(fpOut, &pos);

    ///WAVE_FMT
    pcmFMT.dwSamplePerSec = sample_rate;
    pcmFMT.dwAVgBytePerSec = pcmFMT.dwSamplePerSec * channels * bits / 8;
    pcmFMT.u16BitsPerSample = bits;
    memcpy(pcmFMT.fccID, "fmt ", strlen("fmt "));
    pcmFMT.dwSize = 16;
    pcmFMT.u16BlockAlign = channels * bits / 8;;
    pcmFMT.u16Channels = channels;
    pcmFMT.u16FormatTag = 1;

    fwrite(&pcmFMT, sizeof(WAVE_FMT), 1, fpOut); //先写格式化 ，第二个块
    fgetpos(fpOut, &pos);

    ///WAVE_DATA
    memcpy(pcmData.fccID, "data", strlen("data"));
    pcmData.dwSize = 0;
    fseek(fpOut, sizeof(WAVE_FMT), SEEK_CUR); //下一步就是写裸数据 感觉没有必须，fwrite 已经修改文件指针的位置
    fgetpos(fpOut, &pos);

    fread(&m_pcmData, sizeof(uint16_t), 1, fp);

    while (!feof(fp))
    {
        pcmData.dwSize += 2;
        fwrite(&m_pcmData, sizeof(uint16_t), 1, fpOut);
        fread(&m_pcmData, sizeof(uint16_t), 1, fp);
    }

    pcmHeader.dwSize = 44 + pcmData.dwSize; //确定最终的文件大小

    rewind(fpOut);                                    //将文件内部的指针重新指向一个流的开头，从头开始写
    fgetpos(fpOut, &pos);
    fwrite(&pcmHeader, sizeof(RIFF_Chunk), 1, fpOut); //最后写入第一个块，要点：写入最终文件的长度
    fgetpos(fpOut, &pos);
    fseek(fpOut, sizeof(WAVE_FMT), SEEK_CUR);         //再偏移到第二个块的末尾
    fgetpos(fpOut, &pos);
    fwrite(&pcmData, sizeof(WAVE_DATA), 1, fpOut);    //再写入第三块的数据信息，要点：写入裸数据的长度
    fgetpos(fpOut, &pos);

    fclose(fp);
    fclose(fpOut);

    return 0;
}